# piceofcode


для асинхронной загрузки redux слайсов


import { AnyAction, combineReducers, Reducer, ReducersMapObject } from "@reduxjs/toolkit";
import { MountedReducers, ReducerManager, StateSchemaKey } from "./stateSchema";
import { StateSchema } from "./stateSchema";

export function createReducerManager(initialReducers: ReducersMapObject<StateSchema>): ReducerManager {
   const reducers = { ...initialReducers };

   let combinedReducer = combineReducers(reducers);

   let keysToRemove: Array<StateSchemaKey> = [];
   const mountedReducers: MountedReducers = {};

   return {
      getReducerMap: () => reducers,
      getMountedReducers: () => mountedReducers,
      reduce: (state: StateSchema, action: AnyAction) => {
         if (keysToRemove.length > 0) {
            state = { ...state };
            keysToRemove.forEach(key => {
               delete state[key];
            });
            keysToRemove = [];
         }
         return combinedReducer(state, action);
      },
      add: (key: StateSchemaKey, reducer: Reducer) => {
         if (!key || reducers[key]) {
            return;
         }
         reducers[key] = reducer;
         mountedReducers[key] = true;

         combinedReducer = combineReducers(reducers);
      },
      remove: (key: StateSchemaKey) => {
         if (!key || !reducers[key]) {
            return;
         }

         delete reducers[key];
         keysToRemove.push(key);
         mountedReducers[key] = false;

         combinedReducer = combineReducers(reducers);
      },
   };
}

export const DynamicModuleLoader = (props: DynamicModuleLoaderProps) => {
   const { children, reducers, removeAfterUnmount = true } = props;

|
   const store = useStore() as ReduxStoreWithManager;
   const dispatch = useDispatch();

   useEffect(() => {
      const mountedReducers = store.reducerManager.getMountedReducers();

      Object.entries(reducers).forEach(([name, reducer]) => {
         const mounted = mountedReducers[name as StateSchemaKey];
         if (!mounted) {
            store.reducerManager.add(name as StateSchemaKey, reducer);
            dispatch({ type: `@INIT ${name} reducer` });
         }
      });

      return () => {
         if (removeAfterUnmount) {
            Object.entries(reducers).forEach(([name, reducer]) => {
               store.reducerManager.remove(name as StateSchemaKey);
               dispatch({ type: `@DESTROY ${name} reducer` });
            });
         }
      };
   }, []);

   return <>{children}</>;
};

//применение DynamicModuleLoader
import { useTranslation } from "react-i18next";
import { DynamicModuleLoader, ReducersList } from "Shared";
import { PageInformation } from "Shared/ui/PageInformation";
import { BudgetCommitmentSliceReducer } from "Entities/BudgetCommitments";
import { Outlet } from "react-router";

interface IProps {
   className?: string;
}

const reducers: ReducersList = {
   BudgetCommitment: BudgetCommitmentSliceReducer,
};

const BudgetCommitmentPage = (props: IProps) => {

   const { t } = useTranslation();

   return (
      <DynamicModuleLoader reducers={reducers} removeAfterUnmount={false}>
         <PageInformation pageTitle={t("БЮДЖЕТНЫЕ ОБЯЗАТЕЛЬСТВА")} />
         <Outlet />
      </DynamicModuleLoader>
   );
};

export default BudgetCommitmentPage;





===============================================================================


export const routeConfig: Record<AppRoutes, AppRoutesProps> = {
  [AppRoutes.MAIN]: {
    path: RoutePath.main,
    element: <MainPage />,
  },

  [AppRoutes.ADMIN]: {
    path: RoutePath.admin,
    element: <h1>admin</h1>,
  },

  [AppRoutes.PUBLIC]: {
    path: AppRoutes.PUBLIC,
    element: <Outlet />,
    child: {
      [ChildRoutes.REGISTRATION]: {
        path: ChildRoutePath.registration,
        element: <RegistrationPageLazy />,
        child: {
          ["type"]: {
            path: ":type",
            element: <RegistrationWithTypePageLazy />,
          },
        },
      },
      [ChildRoutes.LOGIN]: {
        path: ChildRoutePath.login,
        element: <LoginPage />,
      },
    },
  },





  ===================================================================


  export const TenderFormationSlice = createSlice({
   name: "TenderFormationSlice",
   initialState,
   reducers: {
      setTender(state, action) {
         state.tenderEntity = action.payload;
      },
      resetTenderEntity(state, action) {
         state.tenderEntity = action.payload;
      },
      setTenderProperty(state, action: PayloadAction<{ key: keyof TenderEntityType; data: any }>) {
         state.tenderEntity = {
            ...state.tenderEntity,
            [action.payload.key]: action.payload.data,
         };
      },
   },




   ===============================================================


   export const COZeroStage = () => {
   const dispatch = useAppDispatch();

   const navigate = useNavigate();

   const [searchParams] = useSearchParams();

   const formRef = useRef<FormInstance>(null);

   const [form] = Form.useForm();

   const { orderMethod, orderView } = useAppSelector(state => state.COZero);
   const tenderEntity = useSelector(getTenderEntitySelector);
   const { newTender, isTenderNew } = useAppSelector(state => state.Tender);

   const handleSetFirstTenderData = () => {
      const tenderData = {
         firstStageId: tenderEntity.id,
         name: tenderEntity.name,
         view: tenderEntity.view,
      };
      Object.entries(tenderData).forEach(([key, data]) => {
         dispatch(setNewTenderProperty({ key, data }));
      });
   };

   const handleCreateButtonClick = () => {
      dispatch(setIsTenderNew(true));

      if (
         [ORDER_METHODS_ENUM.NEGOTIATION_UNLIMITED_PHASE2, ORDER_METHODS_ENUM.FRAMEWORK_AGREEMENT].includes(
            newTender.method,
         )
      ) {
         handleSetFirstTenderData();
      }

      navigate({
         pathname: getRouteStages(),
         search: `?${searchParams}`,
      });

      localStorage.removeItem(LOCAL_STORAGE_KEYS.TENDER_PERSIST);
      localStorage.removeItem(LOCAL_STORAGE_KEYS.CREATE_ORDER_ACTIVE_STEP);
   };

   const selectTenderType = <SelectTenderType orderView={orderView} dispatch={dispatch} />;
   const selectTenderGradeField = <SelectTenderGradeField />;

   const extraFormFileds = new Map([
      [
         ORDER_METHODS_ENUM.TWO_PACKAGE_UNLIMITED,
         <>
            {selectTenderType}
            {selectTenderGradeField}
            {/* <SetMaserContractField /> */}
            <SelectTenderCommissionCountFiled dispatch={dispatch} />
         </>,
      ],
      [
         ORDER_METHODS_ENUM.TWO_PACKAGE_UNLIMITED_FRAMEWORK_AGREEMENT,
         <>
            {selectTenderType}
            {selectTenderGradeField}
            {/* <SetMaserContractField /> */}
            <SelectTenderCommissionCountFiled dispatch={dispatch} />
         </>,
      ],
      [
         ORDER_METHODS_ENUM.NEGOTIATION_UNLIMITED,
         <>
            {selectTenderType}
            {selectTenderGradeField}
         </>,
      ],
      [
         ORDER_METHODS_ENUM.NEGOTIATION_UNLIMITED_PHASE2,
         <>
            <TenderNumberField
               dispatch={dispatch}
               isTenderNew={isTenderNew}
               tenderMethodForCreate={ORDER_METHODS_ENUM.NEGOTIATION_UNLIMITED_PHASE2}
               firstPartTenderMethod={ORDER_METHODS_ENUM.NEGOTIATION_UNLIMITED}
            />
         </>,
      ],
      [
         ORDER_METHODS_ENUM.FRAMEWORK_AGREEMENT,
         <>
            <TenderNumberField
               dispatch={dispatch}
               isTenderNew={isTenderNew}
               tenderMethodForCreate={ORDER_METHODS_ENUM.FRAMEWORK_AGREEMENT}
               firstPartTenderMethod={ORDER_METHODS_ENUM.TWO_PACKAGE_UNLIMITED_FRAMEWORK_AGREEMENT}
            />
         </>,
      ],
      [
         ORDER_METHODS_ENUM.REQUEST_FOR_QUOTATION,
         <>
            {selectTenderType}
            {selectTenderGradeField}
         </>,
      ],
      [
         ORDER_METHODS_ENUM.REQUEST_FOR_QUOTATION_FRAMEWORK_AGREEMENT,
         <>
            {selectTenderType}
            {selectTenderGradeField}
         </>,
      ],
   ]);

   return (
      <Form ref={formRef} name='create-tender-zero-stage' onFinish={handleCreateButtonClick} form={form}>
         <SelectTenderMethod orderMethod={orderMethod} dispatch={dispatch} />

         <Form.Item
            noStyle
            shouldUpdate={(prevValues, currentValues) => prevValues.planTypes !== currentValues.planTypes}>
            {({ getFieldValue }) => {
               return extraFormFileds.get(getFieldValue("tenderMethod"));
            }}
         </Form.Item>

         <Wrapper title=''>
            <Button type='primary' htmlType='submit'>
               {t('Создать объявление')}
            </Button>
         </Wrapper>
      </Form>
   );
};






============================================================================================


const queryParams: Array<keyof TenderEntityType> = ["method", "view", "ballEval", "countCommission"];

const createCommonFields = (isTenderNew: boolean, tenderEntity: TenderEntityType) => [
   <TenderNameInput key='TenderNameInput' isTenderNew={isTenderNew} />,
   <TenderDate key='TenderDate' isTenderNew={isTenderNew} defaultValue={tenderEntity.dateContest} />,
   <SuppliersDate key='SuppliersDate' isTenderNew={isTenderNew} />,
   <ProviderCurrency key='ProviderCurrency' isTenderNew={isTenderNew} />,
   <BidWarrant key='BidWarrant' />,
   <Wrapper key='FileUploadWrapper' title='ИНСТРУКЦИИ ПО ПОДГОТОВКЕ ПРЕДЛОЖЕНИЙ'>
      <FileUploadButton tenderProperty='instructionForPreparingBid' />
   </Wrapper>,
];

const createDisabledFields = (fields: React.ReactElement[]): React.ReactElement[] => {
   return fields.map(field => React.cloneElement(field, { disabled: true }));
};

const createFormFields = (isTenderNew: boolean, tenderEntity: TenderEntityType) => {
   const commonFields = createCommonFields(isTenderNew, tenderEntity);

   return {
      [ORDER_METHODS_ENUM.REQUEST_FOR_QUOTATION]: commonFields,
      [ORDER_METHODS_ENUM.REQUEST_FOR_QUOTATION_FRAMEWORK_AGREEMENT]: commonFields,
      [ORDER_METHODS_ENUM.TWO_PACKAGE_UNLIMITED]: commonFields,
      [ORDER_METHODS_ENUM.TWO_PACKAGE_UNLIMITED_FRAMEWORK_AGREEMENT]: commonFields,
      [ORDER_METHODS_ENUM.FRAMEWORK_AGREEMENT]: createDisabledFields(commonFields),
      [ORDER_METHODS_ENUM.NEGOTIATION_UNLIMITED]: [commonFields[0], commonFields[1]],
      [ORDER_METHODS_ENUM.NEGOTIATION_UNLIMITED_PHASE2]: createDisabledFields(commonFields),
   };
};

export const OrganizationInfoStage = () => {
   const dispatch = useAppDispatch();
   const [searchParams] = useSearchParams();
   const tenderEntity = useSelector(getTenderEntitySelector);
   const { isLoading, isTenderNew, newTender } = useAppSelector(state => state.Tender);
   const { handleNext } = useContext(handleNextContext) as contextTypes;

   useEffect(() => {
      if (!tenderEntity.id) {
         queryParams.forEach(param => {
            const value = searchParams.get(param);
            if (param === "ballEval") {
               dispatch(setNewTenderProperty({ key: param, data: value === ORDER_GRADE_ENUM.BALL || false }));
            } else if (value) {
               dispatch(setNewTenderProperty({ key: param, data: value }));
            }
         });
         dispatch(setIsTenderNew(true));
      }
   }, [dispatch, searchParams, tenderEntity.id]);

   const handleFinish = async () => {
      const processTender = isTenderNew ? createNewTender : updateTenderFirstStage;
      const tenderData = isTenderNew
         ? newTender
         : {
              id: tenderEntity.id,
              name: tenderEntity.name,
              dateContest: tenderEntity.dateContest,
              validnessPeriodOfBid: tenderEntity.validnessPeriodOfBid,
              masterContract: tenderEntity.masterContract,
              allowOtherCurrency: tenderEntity.allowOtherCurrency,
              bidWarranty: tenderEntity.bidWarranty,
              instructionForPreparingBid: tenderEntity.instructionForPreparingBid,
              warrantyProvisionForm: tenderEntity.warrantyProvisionForm,
              allowMonetaryValue: tenderEntity.allowMonetaryValue,
              bidSecurityAmount: tenderEntity.bidSecurityAmount,
              validnessPeriodOfGOTZ: tenderEntity.validnessPeriodOfGOTZ,
           };

      try {
         const resultAction = await dispatch(processTender({ tender: tenderData }));
         if (resultAction.meta.requestStatus === "fulfilled") {
            dispatch(setIsTenderNew(false));
            handleNext();
         }
      } catch (err) {
         console.error("Error during tender processing:", err);
         message.error("Ошибка при обработке тендера");
      }
   };

   const initialValues = fieldsMaker(isTenderNew ? newTender : tenderEntity);
   const formFields = createFormFields(isTenderNew, tenderEntity);
  

   const currentMethod = isTenderNew && newTender.method ? newTender.method : tenderEntity.method;

   return (
      <Form autoComplete='off'  fields={initialValues} onFinish={handleFinish}>
         {
            //@ts-ignore
            formFields[currentMethod]
         }
         <StepsControlBtns isLoading={isLoading} asHandleNext={false} />
      </Form>
   );
};


